Bean Services
The Bean Component is a pluggable container in SwitchYard which allows Java classes (or beans) to provide and consume services. This means that you can implement a service by simply annotating a Java class. It also means you can consume a service by injecting a reference to that service directly into your Java class. Rather than writing our own POJO container to provide this capability, we have implemented the bean component as a Weld extension. No need to learn a new programming model - bean services are standard CDI beans with a few extra annotations. This also opens up the possibilities of how SwitchYard is used; you can now expose existing CDI-based beans in your application as services to the outside world or consume services within your bean.
Providing a Service
Providing a service with the Bean component is as simple as adding an @Service annotation to your bean.
@Service(SimpleService.class)
public class SimpleServiceBean implements SimpleService {
public String sayHello(String message) {
System.out.println("*** Hello message received: " + message);
return "Hi there!!";
}
}
public interface SimpleService {
String sayHello(String message);
}
The SimpleService interface represents the Service Interface, defining the service operations that are exposed by SwitchYard.
The only other thing you need is a META-INF/beans.xml file in your deployed application. When the application is deployed, the Weld runtime scans the application for beans and enlists the SwitchYardCDIServiceDiscovery CDI extension in the CDI lifecycle. Our extension will pick up @Service beans and make them available to the application deployer (will depend on the container). At this point, the service can be invoked from other services within SwitchYard or bound to a wire protocol via SwitchYard gateways.
Service Operations
All SwitchYard Services, no matter what the implementation type, are composed of one or more Service Operations. As you might imagine, in the case of a Bean Service, the Service Operations are the set of Java methods exposed by the Service Interface.
There are a few restrictions when it comes to defining Bean Service Operations:
-
Declares a maximum of one Input type i.e. the Java method signature must have a maximum of one Java parameter.
-
Declares a maximum of one Output type. This is obviously enforced by the Java language since you can only define one return type on a Java method.
-
Declares a maximum of one Fault (Exception) type.
Service Operation Types
All Service Operations on a SwitchYard Service can define an
Input
,
Output
and
Fault
message. These messages have a
type
associated with them, which is defined as a QName. This type is used by the data transformation layer, when trying to work out which transformers to apply to a Message payload.
For bean Services, the default type QName for Input (input param), Output (return value) and Fault (Exception) are derived from the Java class name in each case (param, return, throws). For some types however (e.g. org.w3c.dom.Element), the Java type name alone does not tell you the real type of the data being held by that Java Object instance. For this reason, Bean Service Operations (methods) can be annotated with the @OperationTypes annotation e.g.
public interface OrderService {
@OperationTypes(
in = "{http://acme.com/orders}createOrder",
out = "{http://acme.com/orders}createOrderResult",
fault = "java:com.acme.exceptions.OrderManagementException"
)
Element createOrder(Element order) throws OrderCreateFailureException;
}
It's also possible to set the default data type for a Java type using the @DefaultType Type level annotation. This is useful for setting the type for a hierarchy of Java types. Note in the example code above how we changed the type for OrderCreateFailureException (to "java:com.acme.exceptions.OrderManagementException") by defining a fault type on the @OperationTypes. It's type would otherwise default to "java:com.acme.exceptions.OrderCreateFailureException". We could also do this by annotating the base OrderManagementException class with the @DefaultType annotation. This would set the default type for the OrderManagementException class and all its sub-classes, including OrderCreateFailureException, which would mean not having to defining a fault type on the @OperationTypes wherever one of these exceptions is used on a Bean Service Operation.
SwitchYard Context Injection
Sometimes you need access to the SwitchYard Exchange
Context
instance associated with a given Bean Service Operation invocation. To access this, simply add a
Context
property to your bean and annotate it with the CDI
@Inject
annotation.
@Service(SimpleService.class)
public class SimpleServiceBean implements SimpleService {
@Inject
private Context context;
public String sayHello(String message) {
System.out.println("*** Funky Context Property Value: " + context.getPropertyValue("funkyContextProperty"));
return "Hi there!!";
}
}
Note that you can only make calls on the Context instance within the scope of one of the Service Operation methods. Invoking it outside this scope will result in an UnsupportedOperationException being thrown.
Consuming a Service
Consuming a SwitchYard service from within a CDI bean is done via @Reference annotations.
@Service(ConsumerService.class)
public class ConsumerServiceBean implements ConsumerService {
@Inject
@Reference
private SimpleService service;
public void consumeSomeService(String consumerName) {
service.sayHello("Hello " + consumerName);
}
}
public interface ConsumerService {
void consumeSomeService(String consumerName);
}
Note that the contract for the service is all that's exposed to the bean consumer. The reference could point to a service that is hosted outside of SwitchYard and exposed over JMS, SOAP, FTP, etc. The SwitchYard runtime handles the resolution of the service reference to a concrete service, allowing your service logic to remain blissfully ignorant. Invocations made through this reference are routed through the SwitchYard exchange mechanism.
The @Reference annotation can accept a service name in cases where the default name resolution (interface Class simple name e.g. "OrderService") are not appropriate.
@Reference("urn:myservices:purchasing:OrderService")
private OrderService orders;
This can be useful when the default name resolution is not appropriate. Keep in mind that the name used by the reference is not required to match the target service, but it resolve to a service through some mechanism. For example, the deployed application could contain wiring configuration which maps the service name used by the bean reference to a different service name in the runtime.
Bean Services In a Java EE Web Application Container
As of SwitchYard 0.2.0, applications can be deployed in a JEE Web Application Container such as Tomcat or Jetty i.e. you don't need to use the SwitchYard Application Servers (AS6 or AS7).
Two distinct tasks need to be performed in order to get your CDI based SwitchYard application working in your Web Application Container:
-
Configure the SwitchYard WebApplicationDeployer Servlet Listener in your Web Application.
-
Configure Weld into your Servlet Container.
See the "webapp-deploy" quickstart (in the demos folder) as an example of how to create a CDI Bean based SwitchYard application that can be deployed on Tomcat.
JavaServer Faces
The JavaServer Faces (JSF) technology provides a server-side component framework that is designed to simplify the development of user interfaces (UIs) for Java EE applications. JSF has very tight integration with CDI to provide the Object Model behind the JSF user interface components.
The fact that SwitchYard Bean Services are based on CDI means it's possible to have a really nice integration between SwitchYard Bean Services and a JSF based user interface. This section is not an CDI or JSF reference, but does provide some tips/guidelines on using these technologies within the context of SwitchYard. This list of tips will grow and be refined as more use cases are tested and verified.
JavaServer Faces - General Guidelines
The following guidelines/tips should apply to any container when building a JSF user interface on top of a SwitchYard Service.
Indirect Service Invocation
As with any JSF based user interface, the JSF pages will contain EL tokens referencing scoped CDI beans (by name ala the
@Named
bean annotation). It's possible to annotate your SwitchYard Service CDI beans with the
@Named
annotation and inject them directly into your application's JSF components, but that would result in your JSF pages making direct invocations on the Service implementation (i.e. the Service implementation bean would be injected directly into the JSF components). What you really want to do is to invoke your SwitchYard Services through the SwitchYard
Exchange
mechanism. This reduces the coupling between your JSF components and your SwitchYard Service implementations.
Currently, the only way to invoke a SwitchYard CDI Bean Service through the SwitchYard Exchange mechanism is to invoke them through a @Reference injected Service reference (other options may be available in future). This provides a client side proxy bean that handles all the SwitchYard Exchange invocation magic for all the operations exposed by the Service in question. The catch here is that these proxy beans are not available (through CDI) to the JSF components, so you need a bog standard named (@Named) CDI bean containing an injected @Reference sitting between the JSF components and the SwitchYard CDI Bean Services. This is not really a problem though, because your JSF components will likely have a more natural interaction with a business/model type value-object bean (getters/setters) than they will with a Service interface type bean (operations). So, a layer of indirection will probably make sense anyway.
So if you consider an example of an OrderService (Service implementation not shown):
public interface OrderService {
OrderAck submitOrder(Order order);
}
public class Order implements Serializable {
private String orderId;
private String itemId;
private int quantity = 1;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getItemId() {
return itemId;
}
public void setItemId(String itemId) {
this.itemId = itemId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
}
Your JSF components will probably have a more natural interaction with the Order bean than with the OrderService e.g.:
<div id="content">
<h1>New Order</h1>
<div style="color: red">
<h:messages id="messages" globalOnly="false" />
</div>
<h:form id="newOrder">
<div>
Order ID:
<h:inputText id="orderID" value="#{order.orderId}" required="true"/>
<br/>
Item ID:
<h:inputText id="itemID" value="#{order.itemId}" required="true"/>
<br/>
Quantity:
<h:inputText id="quantity" value="#{order.quantity}" required="true"/>
<p/>
<h:commandButton id="createOrder" value="Create" action="#{order.create}"/>
</div>
</h:form>
</div>
However, in order to make this work, we need to make a few tweaks to the Order bean:
-
Annotating it with @Named and @RequestScoped.
-
Adding a @Reference to the OrderService.
-
Implementing the create method that invokes the OrderService reference (Exchange proxy).
@Named
@RequestScoped
public class Order implements Serializable {
@Inject
@Reference
private OrderService orderService;
private String orderId;
private String itemId;
private int quantity = 1;
public String getOrderId() {
return orderId;
}
public void setOrderId(String orderId) {
this.orderId = orderId;
}
public String getItemId() {
return itemId;
}
public void setItemId(String itemId) {
this.itemId = itemId;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public void create() {
OrderAck serviceAck = orderService.submitOrder(this);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(serviceAck.toString()));
}
}
To reiterate, because of the @Reference annotation, the orderService property instance is not a reference to the actual Service implementation. Instead, it is a SwitchYard Exchange proxy to that Service implementation.
Also note that by using @Reference injected Service references, your backend Service implementation can be a non CDI Bean Service implementation e.g. a Camel Routing Services. This mechanism opens up all sorts of integration possibilities!
See the "orders" quickstart (in the demos folder) as an example of how to provide a JSF user interface on top of a SwitchYard CDI Bean Service deployed on SwitchYard AS7.
Camel Services
Camel services allow you to leverage the core routing engine inside of Apache Camel to route between services in SwitchYard. The route itself is exposed as a service within SwitchYard, which means it has a well-defined contract and can be injected into any other service in the runtime. The routing logic can be expressed in XML and included directly in the service component configuration, or you can use the Java DSL to define the route in an annotated Java class.
Java DSL Routes
SwitchYard provides an
@Route
annotation which can be used to declare that a class contains a Camel route which should be represented as a service in SwitchYard. The @Route annotation has a single required element which identifies the service interface to be used by the route. Aside from this annotation, the class looks exactly like a route that you would define in Camel alone.
@Route(JavaDSL.class)
public class JavaDSLBuilder extends RouteBuilder {
public void configure() {
from("switchyard://JavaDSL")
.log("Message received in Java DSL Route")
.log("${body}")
.split(body(String.class).tokenize("\n"))
.filter(body(String.class).startsWith("sally:"))
.to("switchyard://XMLService?operationName=acceptMessage");
}
}
When an application containing one or more Java DSL routes is built, the SwitchYard Maven plugin will automatically generate the required service component configuration in META-INF/switchyard.xml. The above route would produce the following configuration:
<component name="JavaDSLBuilder">
<implementation.camel xmlns="urn:switchyard-component-camel:config:1.0">
<java class="org.switchyard.quickstarts.camel.service.JavaDSLBuilder"/>
</implementation.camel>
<service name="JavaDSL">
<interface.java interface="org.switchyard.quickstarts.camel.service.JavaDSL"/>
</service>
</component>
The "from" endpoint in a Camel service must always equal the service name. This allows SwitchYard to establish service bindings outside the route definition itself.
XML Routes
Configuring a Camel service using XML is done using the <implementation.camel> configuration directly as follows:
<sca:component name="CamelComponent">
<implementation.camel xmlns="urn:switchyard-component-camel:config:1.0">
<route xmlns="http://camel.apache.org/schema/spring" id="Camel Test Route">
<log message="ItemId [${body}]"/>
<to uri="switchyard://WarehouseService?operationName=hasItem"/>
<log message="Title Name [${body}]"/>
</route>
</implementation.camel>
<sca:service name="OrderService">
<sca:interface.java interface="org.switchyard.component.camel.deploy.support.OrderService"/>
</sca:service>
<sca:reference name="WarehouseService">
<sca:interface.java interface="org.switchyard.component.camel.deploy.support.WarehouseService"/>
</sca:reference>
</sca:component>
You'll notice in the above configuration that the route in the <implementation.camel> does not specify a from endpoint on the route definition. For Camel XML routes, Switchyard automatically adds this for the service. The above would be invokable by using the following code (snippet from a SwitchYard test):
String title = newInvoker("OrderService").operation("getTitleForItem").sendInOut("10").getContent(String.class);
Running the above code snippet would generate the following in you console log:
10:57:45,915 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) started in 0.838 seconds
10:57:46,284 INFO [impl.DefaultCamelContext] Route: Camel Test Route started and consuming from: Endpoint[switchyard://OrderService]
10:57:46,307 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) is starting
10:57:46,307 INFO [impl.DefaultCamelContext] Total 1 routes, of which 1 is started.
10:57:46,307 INFO [impl.DefaultCamelContext] Apache Camel 2.6.0 (CamelContext: camel-1) started in 0.000 seconds
10:57:46,428 INFO [Camel Test Route] ItemId [10]
10:57:46,434 INFO [Camel Test Route] Title Name [Fletch]
SwitchYard Endpoints
The Camel component contains not only wraps Camel, it defines a Camel component itself which maps to switchyard:// endpoint URIs. As seen in the above examples, the switchyard:// endpoint scheme can be used to route between Camel routes and SwitchYard services. Endpoint configuration is very straightforward:
switchyard://[service-name]?operationName=[operation-name]
-
service-name : name of the SwitchYard service
-
operation-name : name of the service operation to be invoked. This is only used on references and is optional if the target service only has a single operation.
CDI Integration
SwitchYard integrates the CDI Bean Registry with the SwitchYard Camel component. This means that, within SwitchYard Camel Route definitions, you can reference named (@Named) CDI Beans.
Consider an example of where you have the following CDI bean:
@Named
@ApplicationScoped
public class StringUtil {
public String trim(String string) {
return string.trim();
}
// Other utilities...
}
This bean can be used inside your SwitchYard Camel Routes as follows:
@Route(JavaDSL.class)
public class JavaDSLBuilder extends RouteBuilder {
public void configure() {
from("switchyard://JavaDSL")
.split(body(String.class).tokenize("\n"))
.filter(body(String.class).startsWith("sally:"))
.bean(StringUtil.class, "trim(String)");
}
}
See Camel's Bean Binding documentation for more details.
Knowledge Services
Knowledge Services are SwitchYard Services that leverage KIE, and thus, Drools and/or jBPM:
Given that Drools and jBPM are very tightly integrated under KIE, much of their runtime configuration can be shared by both SwitchYard's BPM component and its Rules component. Therefore, documentation on this page refers to elements that are either exactly, or structurally, identical to both the BPM and Rules components. That is to say, any configuration element you see here can be used in the same way for both BPM and Rules. Sometimes, however, the context of the element is significant, so cases like that will be identified where applicable.
Note
For some of the configuration elements below, you might wonder why it is a shared element between both the BPM and Rules components. For example, Channels are something only applicable to Rules, right? Yes, however what if your BPM process has a node which executes business rules, and those rules reference channels? In that case, you need a way to configure channels for the rules within your process. Thus, it is documented here.
In each description below, you will see an XML section and an Annotation section. When using the SwitchYard tooling for Eclipse, the switchyard.xml will be generated for you. You can also hand-edit this file. Alternatively, you can annotate your service interfaces with annotations, and use SwitchYard’s Maven plugin to auto-generate the switchyard.xml for you, based on those annotations. If using the plugin, you will have to configure it with either and/or both the BPMSwitchYardScanner or the RulesSwitchYardScanner.
Actions
Actions are how Knowledge Services know how to map service operation invocations to their appropriate runtime counterparts. For example, when method "myOperation" is called, what should happen? Execute some business rules? Start a business process?
Using the XML below as reference, when the SwitchyYard service’s "myOperation" operation is invoked, an action of type "ACTION_TYPE" will be taken. Note that "ACTION_TYPE" is just a placeholder here. Actual ActionTypes are specific to the BPM and Rules components. Please refer to the specific documentation on those pages.
At this time, the id attribute is only applicable to the Rules component.
Please see the Mapping section below for an explanation of the globals, inputs, and outputs sections.
XML
<actions>
<action id="myId" operation="myOperation" type="ACTION_TYPE">
<globals>
<mapping/>
</globals>
<inputs>
<mapping/>
</inputs>
<outputs>
<mapping/>
</outputs>
</action>
</actions>
Annotation
The BPM and Rules components have specific action annotations that map to specific ActionTypes. Please refer to the specific documentation on those pages.
Mappings
Mappings are the way to move data in or out of the action for that operation. You can specify as many mappings as you like for an action, and they get grouped as globals, inputs or outputs:
-
Global mappings are used to provide data that is applicable to the entire action, and is often used in classic in/out param (or data-holder/provider) fashion. An example of a global mapping is a global variable specified within a Drools Rule Language (DRL) file.
-
Input mappings are used to provide data that represents parameters being fed into an action. An example of an input mapping for BPM could be a process variable used while starting a business process. For Rules, it could be a fact to insert into a rules engine session.
-
Output mappings are used to return data out of an action. An example of an output mapping would be a BPM process variable that you want to set as the outgoing (response) message’s content.
Currently, the only supported expressionType is MVEL, which is the default, so you don’t have to specify it. The expression itself can be any MVEL expression, and variables that are available to you by default are:
-
exchange - The current org.switchyard.Exchange.
-
context - The current org.switchyard.Context.
-
message - The current org.switchyard.Message.
Whatever the resultant value of the expression is constitutes the data that is made available to the action. Some examples:
-
expression="message.content" - This is the same as message.getContent().
-
expression="context[‘foo’]" scope="IN" - This is the same as context.getProperty("foo", Scope.IN).getValue(), in a null-safe manner.
Note
Specifying the scope attribute only matters if you use the context variable inside your expression. If you don’t specify a scope, the default Context access (which is done like a Map, if you picked up on that), is done with Scope.EXCHANGE for global mappings, Scope.IN for input mappings, and Scope.OUT for output mappings.
Specifying the variable attribute is often optional, but this depends on the usage. For example, if you are specifying a global variable for a rule, or a process variable to put into (or get out of) a BPM process, then it is required. However, if the result of the expression is to be used as facts for rule session insertion, than specifying a variable name isn’t applicable.
XML
<mapping expression="theExpression" expressionType="MVEL" scope="IN" variable="theVariable"/>
Annotation
@Mapping(expression="theExpression", expressionType=ExpressionType.MVEL, scope=Scope.IN, variable="theVariable")
Channels
Drools supports the notion of "Channels", which are basically "exit points" in your DRL. Here is an example:
package com.example
rule "example rule"
when
$f : Foo ( bar > 10 )
then
channels["Bar"].send( $f.getBar() );
end
XML
<channels>
<channel class="com.example.BarChannel" name="Bar"/>
</channels>
Annotation
channels={
@Channel(value=BarChannel.class, name="Bar")
}
Warning
Channels must implement org.kie.runtime.Channel.
SwitchYard Service Channel
SwitchYard provides an out-of-the-box Channel which allows you to invoke (one-way) other SwitchYard services directly and easily from your DRL. Here is an example:
XML
<channel name="HelloWorld" reference="HelloWorld" operation="greet"/>
Annotation
import org.switchyard.component.common.knowledge.channel.SwitchYardServiceChannel;
// ...
@Channel(value=SwitchYardServiceChannel.class, name="HelloWorld", reference="HelloWorld", operation="greet")
Attribute Reference:
-
value (annotation) / class (xml) = The channel implementation class. (Default is SwitchYardServiceChannel.)
-
name = The channel name. (default = simple name of the implementation class)
-
reference = The service reference qualified name.
-
operation = The service reference operation name.
-
input = The service reference operation input name.
-
interfaze (annotation) = The service reference interface class name.
Listeners
Listeners are used to monitor specific types of events that occur during Knowledge execution. An example of this would be to audit a BPM process, and save the audit details into a database while the process progresses. Then at a later time, these details can be reported against. There are many out-of-the-box Listeners that Drools and jBPM provide, and you can write your own. The only restriction in writing your own Listener is that it must, at the minimum, implement java.util.EventListener. However, your Listener won’t actually be registered for anything unless it also implements one of the respected KIE/Drools/jBPM Listener interfaces. For example, org.drools.event.WorkingMemoryEventListener, org.drools.event.AgendaEventListener, org.kie.event.process.ProcessEventListener, or similar.
Note
If the Listeners provide a Constructor taking a single KieRuntimeEventManager (or KnowledgeRuntimeEventManager) as a parameter, that is used and it is assumed it will do the work of registering itself with the passed-in event manager (OOTB {{WorkingMemoryLogger}}s do this). Otherwise, a no-arg constructor is assumed, and SwitchYard will do the work of registering the Listeners for each of the respected interfaces it implements.
XML
<listeners>
<listener class="org.drools.event.DebugProcessEventListener"/>
<listener class="org.kie.event.rule.DebugWorkingMemoryEventListener"/>
<listener class="com.example.MyListener"/>
</listeners>
Annotation
listeners={
@Listener(DebugProcessEventListener.class),
@Listener(DebugWorkingMemoryEventListener.class),
@Listener(MyListener.class)
}
Loggers
Loggers are special types of Listeners, and are used to output the events that occur during Knowledge execution. Support for Loggers is done using a dedicated configuration element. Events can be logged to the CONSOLE or to a FILE (or THREADED_FILE). If they are directed to a file, that log can later be opened via the Drools Eclipse tooling.
XML
<loggers>
<logger interval="2000" log="myLog" type="THREADED_FILE"/>
<logger type="CONSOLE/>
</loggers>
Annotation
loggers={
@Logger(interval=2000, log="myLog", type=LoggerType.THREADED_FILE),
@Logger(type=LoggerType.CONSOLE)
}
Manifest
The only configuration element more important than Actions is the Manifest, which is where you specify where the "intelligence" of the component comes from. For the BPM component, this will be, at the minimum, the location of the BPMN 2 process definition file. For the Rules component, this will most likely be the location of DRL, DSL, DSLR or XLS files. There are two ways to to configure the Manifest:
-
With a KIE Container. This relies upon the existence of a META-INF/kmodule.xml configuration file.
-
With a manually defined list of Resources.
Warning
These two options are mutually exclusive: You have to choose one or the other!
The following examples assume there is a DRL file located at classpath: com/example/MyRules.drl
Option 1 (KIE Container):
META-INF/kmodule.xml
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="com.example">
<ksession name="my-session"/>
</kbase>
</kmodule>
XML
<manifest>
<container sessionName="my-session"/>
</manifest>
Annotation
@Manifest(container=@Container(sessionName="my-session"))
In addition to the sessionName attribute, you can also specify baseName and releaseId, if desired.
Also, scanning for updates is supported only with the container option (not the resources option). To enable this, simply set scan="true" and, optionally, scanInterval=<# of milliseconds>.
Option 2 (Resources):
XML
<manifest>
<resources>
<resource location="com/example/MyProcess.bpmn" type="BPMN2"/>
<resource location="com/example/MyRules.drl" type="DRL"/>
</resources>
</manifest>
Annotation
@Manifest(resources={
@Resource(location="com/example/MyProcess.bpmn" type="BPMN2"),
@Resource(location="com/example/MyRules.drl" type="DRL")
})
Using a Repository
If your resources are stored in a repository like Guvnor, they can be loaded remotely. To do so, configure a ChangeSet.xml file as a resource:
<resource location="ChangeSet.xml" type="CHANGE_SET"/>
Then, inside the ChangeSet.xml file, specify the the details of accessing the remote package:
<change-set xmlns='http://drools.org/drools-5.0/change-set' xmlns:xs='http://www.w3.org/2001/XMLSchema-instance' xs:schemaLocation='http://drools.org/drools-5.0/change-set http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd'>
<add>
<resource source='http://localhost:8080/drools-guvnor/org.drools.guvnor.Guvnor/package/sandbox.michele/LATEST' type='PKG' basicAuthentication="enabled" username="admin" password="admin"/>
</add>
</change-set>
Properties
Properties are the way to provide "hints" to the underlying KIE/Drools/jBPM runtime on how certain options are configured. Rather than having to expose every single KIE/Drools/jBPM option as a configurable element or attribute within SwitchYard, they can be set as open-ended properties.
Properties are an advanced topic, so setting them should be done with care. All possible property names and values will not be listed here, but as a starting point, in your IDE open up a Type Heirarchy with a root of org.kie.conf.Option. That is your full list. Here are just a couple examples:
XML
<properties>
<property name="drools.clockType" value="pseudo"/>
<property name="drools.eventProcessingMode" value="stream"/>
</properties>
Annotation
private static final String clockTypeName = org.kie.runtime.conf.ClockTypeOption.PROPERTY_NAME;
private static final String clockTypeValue = org.drools.ClockType.PSEUDO_CLOCK.value();
private static final String eventProcessingName = org.kie.conf.EventProcessingOption.PROPERTY_NAME;
private static final String eventProcessingValue = org.kie.conf.EventProcessintOption.STREAM.getMode();
properties={
@Property(name=MyClass.clockTypeName, value=MyClass.clockTypeValue),
@Property(name=MyClass.eventProcessingName, value=MyClass.eventProcessingValue)
}
BPM Services
The BPM Component is a pluggable container in SwitchYard which allows a business process to be exposed as a service. One fronts their business process with a custom interface and, if desired, can easily annotate it's methods to define which should start a process, signal a process event, or abort a process.
Note
Configuration of SwitchYard's BPM Component has been overhauled greatly between 0.6 and 0.7. Documentation on this page reflects 0.7. The wiki article: "BPM & Rules Component Configuration Changes" is a useful resource to consult.
Providing a Service
To provide a service with the BPM component, you will have to:
-
Define your process using BPMN2.
-
Create a java interface, fronting your BPMN2 process, which can be exposed to other services, and/or to your binding(s).
-
Add the component containing the implementation and service interface to the SwitchYard configuration. This step can be automated using annotations and the SwitchYard plugin, explained below.
Here is an example of the component section of the SwitchYard configuration:
<component name="MyService">
<implementation.bpm xmlns="urn:switchyard-component-bpm:config:1.0" processId="MyService">
<manifest>
<resources>
<resource location="META-INF/MyService.bpmn" type="BPMN2">
</resources>
</manifest>
<workItemHandlers>
<workItemHandler class="org.switchyard.component.bpm.work.SwitchYardServiceWorkItemHandler" name="SwitchYard Service"/>
</workItemHandlers>
</implementation.bpm>
<service name="MyService">
<interface.java interface="org.switchyard.userguide.MyService"/>
</service>
</component>
The MyService interface can be as simple as this, with no SwitchYard-specific imports:
package org.switchyard.userguide;
public interface MyService {
public void start(String data);
}
However, if you want to have the SwitchYard configuration for this section auto-generated for you, you can use annotations and the SwitchYard Maven Plugin. To do this, you can either add SwitchYard-specific annotations to your interface directly, or (and this is the recommended approach) you can create a separate interface for this purpose. Let's do that:
package org.switchyard.userguide;
import org.switchyard.component.bpm.annotation.BPM;
import org.switchyard.component.bpm.work.SwitchYardServiceWorkItemHandler;
import org.switchyard.component.common.knowledge.annotation.Manifest;
import org.switchayrd.component.common.knowledge.annotation.Resource;
@BPM(
value=MyService.class,
processId="MyService",
manifest=@Manifest(resources={
@Resource(location="META-INF/MyService.bpmn", type="BPMN2")
}),
workItemHandlers={@WorkItemHandler(value=SwitchYardServiceWorkItemHandler.class)}
)
public interface MyServiceProcess {}
At this point you might be wondering what the resources and workItemHandlers attributes of the annotations are.
First, a Resource represents an additional artifact that is required by your BPM process. It could be anything you could think of, including a properties file, a Drools Rule Language file, or whatever. But it needs to be available to the BPM component's runtime for that process. You can either add the resource to the XML yourself, or use the annotation, similar to the examples above.
Next, a WorkItemHandler is a way for you to add your own code into the business process. Simply implement the org.kie.runtime.process.WorkItemHandler interface, and add it to the SwitchYard configuration, either manually in XML, or by referencing your implementation via the workItemHandlers attribute of the BPM annotation, similarly to how we did with resources above.
By default, the SwitchYardServiceWorkItemHandler is always added for you by the SwitchYard Maven plugin. That handler allows you to easily call out to other SwitchYard services by name within your business process. Please see the section "Consuming a BPM Service" below for more information.
Process Interaction
In addition to the interface-level Process annotation, there are also three method-level annotations available to you. Here is an example:
package org.switchyard.userguide;
public interface MyService {
public void start(String data);
public void signal(String data);
public void stop(String data);
}
package org.switchyard.userguide;
import org.switchyard.component.bpm.annotation.AbortProcessInstance;
import org.switchyard.component.bpm.annotation.BPM;
import org.switchyard.component.bpm.annotation.SignalEvent;
import org.switchyard.component.bpm.annotation.StartProcess;
@BPM(MyService.class)
public interface MyServiceProcess extends MyService {
@StartProcess @Override
public void start(String data);
@SignalEvent("myEvent") @Override
public void signal(String data);
@AbortProcessInstance @Override
public void stop(String data);
}
This will then create the following SwitchYard configuration (note that the default processAction type is START_PROCESS for a method, which is why in our original version of this example we didn't have to use the @StartProcess annotation):
<component name="MyService">
<implementation.bpm xmlns="urn:switchyard-component-bpm:config:1.0" processId="MyService">
<actions>
<action operation="start" type="START_PROCESS"/>
<action id="myEvent" operation="signal" type="SIGNAL_EVENT"/>
<action operation="stop" type="ABORT_PROCESS_INSTANCE"/>
</actions>
<manifest>
<resources>
<resource location="META-INF/MyService.bpmn" type="BPMN2"/>
</resources>
</manifest>
<workItemHandlers>
<workItemHandler class="org.switchyard.component.bpm.work.SwitchYardServiceWorkItemHandler" name="SwitchYard Service"/>
</workItemHandlers>
</implementation.bpm>
<service name="MyService">
<interface.java interface="org.switchyard.userguide.MyService"/>
</service>
</component>
@StartProcess
Methods annotated this way (or have the associated xml) will start new process instances.
When you start your process (actually, any interaction with a service whose implementation is bpm), the processInstanceId will be put into the Switchyard Context at Scope.EXCHANGE, and will be fed back to your client in a binding-specific way. For soap, it will be in the form of a soap header in the soap response envelope:
<soap:Header>
<bpm:processInstanceId xmlns:bpm="urn:switchyard-component-bpm:bpm:1.0">1</bpm:processInstanceId>
</soap:Header>
In future process interactions, you will need to send back that same processInstanceId, so that the correlation is done properly. For soap, that means including the same soap header that was returned in the response to be sent back with subsequent requests.
Important
If you are using persistence, the sessionId will also be available in the Context, and will need to be fed back as well. It would look the same way in the soap header as the processInstanceId does.
@SignalEvent
Methods annotated this way (or have the associated xml) will have the associated process instance signaled. As above, the processInstanceId will need to have been available in the Context so the correct process instance is correlated.
There are two other pieces of information when signaling an event:
-
The "id". In BPMN2 lexicon, this is known as the "signal id", but in jBPM can also be known as the "event type". This is set as the id of the annotation.
-
Note: In BPMN2, a signal looks like this: <signal id="foo" value="bar"/> In jBPM, it is the signal id that is respected, not the name. This might require you to tweak a tooling-generated id to whatever you want it called.
-
The "event object". This is the data representing the event itself. There are two ways to pass in the event object:
-
Via a Context object, passed similar to the processInstanceId, known as "signalEvent". If this is the case, you are limited to a String type.
-
Via the Message content object itself (your payload). If the signalEvent Context property is absent, the content of the Message is used as the event object.
@AbortProcessInstance
Methods annotated this way (or have the associated xml) will cause associated process instances to be aborted. As above, the processInstanceId will need to have been available in the Context so the correct process instance is correlated.
Auditing a Process
Please see the Listeners and Loggers sections found in the Knowledge Services documentation.
Process Variables
Important
For you to be able to use variables inside your process, you have to declare your variable names at the process level, as well as in the Parameter Mapping (and possibly Result Mapping) of any process node that wants access to those variables. This can be done easily in the jBPM Eclipse tooling by using the Properties view when clicking on either the whitespace around the process, or on any of your process nodes. For more information, please refer to the jBPM documentation.
Mapping Parameter/Result Variables
Mapping variables from your SwitchYard Exchange, Context or Message into jBPM process variables can be done via expressions, the default (and only currently supported) type is MVEL. This can be done via the following XML:
<implementation.bpm ...>
<actions>
<action ...>
<inputs>
<mapping expression="exchange.serviceName.localPart" expressionType="MVEL" variable="service"/>
<mapping expression="context['org.switchyard.messageId']" variable="messageId" contextScope="IN"/>
<mapping expression="message.content" variable="contentIn"/>
</inputs>
<outputs>
<mapping expression="message.content" variable="contentOut"/>
</outputs>
</action>
</actions>
...
</implementation.bpm>
The same can be done via annotations:
@StartProcess(...
inputs={
@Mapping(expression="exchange.serviceName.localPart", expressionType=ExpressionType.MVEL, variable="service"),
@Mapping(expression="context['org.switchyard.messageId']", variable="messageId", contextScope=Scope.IN),
@Mapping(expression="message.content", variable="contentIn")},
outputs={
@Mapping=(expression="message.content", variable="contentOut")
})
public MyDataResponse process(MyData data);
For more information on mapping variables, please see the Mappings sections of the Knowledge Services documentation.
Consuming a Service
There are two ways of consuming Services with the SwitchYard BPM component:
-
Invoking the BPM implementation through a gateway binding. Since the BPM component exposes a java interface fronting the business process, you can use any of the bindings provided by SwitchYard. This could be a SOAP Binding or a Camel Binding, for example. (Please refer to those sections of this guide for more information.)
-
Invoking other SwitchYard Services from inside a BPM process itself. To do this, you can use the SwitchYardServiceWorkItemHandler, which is provided out-of-the-box. To make authoring BPMN2 processes easier, SwitchYard provides a new widget for the Eclipse BPMN2 Modeler visual editor palette. Here is a screenshot from the "Help Desk" demo quickstart:
On the bottom right hand side under "Custom Task", you can see the SwitchYard Service widget. On the left hand side, you can see the various points of the business process where SwitchYard Services are being invoked. Once you have dropped a SwitchYard Service task in the main window, you can customize it via the Eclipse Properties Editor.
SwitchYard Service Task Properties
The following are properties you can use to configure the SwitchYard Service task:
Service Naming Properties
-
ServiceName: (required)
The name of the SwitchYard service to invoke.
-
ServiceOperationName: (optional; default=use the single method name in the service interface, if there is just one)
The name of the operation within the SwitchYard service to invoke.
Content I/O Properties
-
ContentInputName: (optional; default=contentInput) The process variable which the message content will be placed in.
-
ContentOutputName: (optional; default=contentOutput) The process variable which the message content will be gotten from.
Fault-Handling Properties (see SwitchYard Service Fault Handling below)
-
FaultResultName: (optional)
The name of the output parameter (result variable) the fault (Exception) will be stored under.
-
FaultSignalId: (optional)
The bpmn signal id ("event type" in jbpm lingo) that will be used to signal an event in the same process instance. The event object will be the fault (Exception). Please see
@SignalEvent
above.
-
FaultWorkItemAction: (optional; default=null) After a fault occurs, what should be done? If null, nothing is done. If complete, the current work item (SwitchYard Service task) is completed. If abort, the current work item is aborted.
You can read a more detailed explanation of the Help Desk quickstart demo, as well as how to set up Eclipse to make use of new widget, on this wiki page.
SwitchYard Service Fault Handling
During your process execution, the SwitchYardServiceWorkItemHandler class is used to handle Switchyard Service tasks. It executes service references, per the properties specified in the panel above. Pay particular attention to the list of Fault-Handling Properties. These properties define the behavior of the SwitchYardServiceWorkItemHandler in the case were a fault is encountered when executing the service reference. There are 2 scenarios that the fault-handling covers:
-
"In my process flow, after the SwitchYard Service task, I would like a split gateway where I can inspect a process variable (a "flag") that can tell me that a fault occurred, so that I can diverge one way or another."
-
To do this, specify the FaultResultName property. The SwitchYardServiceWorkItemHandler will make the fault itself available as an output parameter of the task, so that it can be associated with a process variable, and inspected for existence in your split gateway. You must also set the FaultWorkItemAction property to complete, so that the process will continue on to your split gateway!
-
An example bpmn2 process can be found in our JUnit test suite here: BPMWorkTests-FaultResultProcess.bpmn
-
"In my process flow, I have multiple places where faults could occur, and I want to have one shared path of fault-handling."
-
To do this, specify the FaultSignalId property. This must match a signal id you specify in your bpmn2 process definition. You can then add an event node in your process that is triggered with this signal id; the flow from that event node on is your fault-handling path. The SwitchYardServiceWorkItemHandler will then signal the proper event with the configured id.
-
An example bpmn2 process can be found in our JUnit test suite here: BPMWorkTests-FaultEventrocess.bpmn
Whether you choose scenario 1 or 2 above, the question remains "What next?" If you don't specify a FaultWorkItemAction property, nothing is done. The work item is not completed, nor is it aborted. You can set the property to complete to complete the work item after a fault, or you can set the property to abort to abort the work item after a fault.
Important
The FaultWorkItemAction property was added in SwitchYard 0.8, replacing the CompleteAfterFault property of 0.7 and earlier, at which point the default was to complete the work item.
Rules Services
The Rules Component is a pluggable container in SwitchYard which allows business rules to be exposed as a service. One fronts their rules with a custom interface and, if desired, can easily annotate it's methods to define which should execute the rules.
Note
Configuration of SwitchYard's Rules Component has been overhauled greatly between 0.6 and 0.7. Documentation on this page reflects 0.7. The wiki article: "BPM & Rules Component Configuration Changes" is a useful resource to consult.
Providing a Service
To provide a service with the Rules component, you will have to:
-
Define your rules. The Rules Component currently supports Drools as the rule engine. Even though it is quite simple to write rules in Drools, that project's developer tooling and business analyst tooling are very mature.
-
Create a java interface, fronting your rules, which can be exposed to other services, and/or to your binding(s).
-
Add the component containing the implementation and service interface to the SwitchYard configuration. This step can be automated using annotations and the SwitchYard plugin, explained below.
Here is an example of the component section of the SwitchYard configuration:
<component name="MyService">
<implementation.rules xmlns="urn:switchyard-component-rules:config:1.0">
<actions>
<action operation="process" type="EXECUTE"/>
</actions>
<manifest>
<resources>
<resource location="/org/switchyard/userguide/MyService.drl" type="DRL"/>
</resources>
</manifest>
</implementation.rules>
<service name="MyService">
<interface.java interface="org.switchyard.userguide.MyService"/>
</service>
</component>
The MyService interface can be as simple as this, with no SwitchYard-specific imports:
package org.switchyard.userguide;
public interface MyService {
public void process(MyData data);
}
However, if you want to have the SwitchYard configuration for this section auto-generated for you, you can use annotations and the SwitchYard Maven Plugin. To do this, you can either add SwitchYard-specific annotations to your interface directly, or (and this is the recommended approach) you can create a separate interface for this purpose. Let's do that:
package org.switchyard.userguide;
import org.switchyard.component.common.knowledge.annotation.Manifest;
import org.switchyard.component.common.knowledge.annotation.Resource;
import org.switchyard.component.rules.annotation.Execute;
import org.switchyard.component.rules.annotation.Rules;
@Rules(
value=MyService.class,
manifest=@Manifest(resources={
@Resource(location="/org/switchyard/userguide/MyService.drl", type="DRL")
})
)
public interface MyServiceRules extends MyService {
@Execute @Override
public void process(MyData data);
}
That will create the SwitchYard configuration you saw above!
A few important points:
-
In the original switchyard xml, the <actions/> element is optional. If not specified, the behavior is to execute the rules.
-
You will have to configure the switchyard maven plugin with the RulesSwitchYardScanner.
-
You can list as many resources as the Drools knowledge session requires within the manifest. For more information, please see the Manifest section of the Knowledge Services documentation.
-
You don't have to specify the drl resource using annotations. You can create a switchyard.xml file with the drl defined (as shown in the first example), and the plugin will merge what it found via annotations with what you provided in a preliminary switchyard.xml file.
Stateless vs. Stateful Rules Execution
By default, service method invocation will create a new Drools knowledge session, execute it given the passed-in domain data, and then be cleanly disposed.
However, it is possible to configure SwitchYard so that a stateful knowledge session will be used. Specifically, it will "stick around" spanning multiple invocations. (Please visit the Drools documentation to learn more about stateless vs. stateful knowledge sessions.) To do this, you use the @FireAllRules annotation instead of @Execute. Or similarly in the switchyard.xml, use the FIRE_ALL_RULES action type instead of EXECUTE.
There is also a new capability which allows you to insert facts into a stateful knowledge session without firing the rules (you might want to do that later). In this case, use the @Insert annotation. Or similarly in switchyard.xml, use the INSERT action type.
Mapping Global Variables
Mapping variables from your SwitchYard Exchange, Context or Message into Drools Globals can be done via expressions, the default (and only currently supported) type is MVEL. This can be done via annotions:
@Execute(... globals={
@Mapping(expression="exchange.serviceName.localPart", expressionType=ExpressionType.MVEL, variable="service"),
@Mapping(expression="context['org.switchyard.messageId']", variable="messageId", contextScope=Scope.IN),
@Mapping(expression="message.content", variable="payload")
})
public void process(MyData data);
or via XML:
<implementation.rules ...>
<actions>
<action operation="process" type="EXECUTE">
<globals>
<mapping expression="exchange.serviceName.localPart" expressionType="MVEL" variable="service"/>
<mapping expression="context['org.switchyard.messageId']" variable="messageId" contextScope="IN"/>
<mapping expression="message.content" variable="payload"/>
</globals>
</action>
</actions>
...
</implementation.rules>
Your expression can use the variables exchange (org.switchyard.Exchange), context (org.switchyard.Context) or message (org.switchyard.Message). You can see in the example above is that context can be accessed in the expression as a java.util.Map. When accessing it as such, the default properties Scope is IN. This can be overridden using the contextScope attribute by changing it to OUT or EXCHANGE.
Now you can use these global variables in your Drools DRL:
package example
global java.lang.String service
global java.lang.String messageId
global com.example.Payload payload
rule "Example"
when
...
then
...
System.out.println("service: " + service + ", messageId: " + messageId + ", payload: " + payload);
end
Of course, in a more realistic scenario, the payload would be accessed in rule firing because it was inserted into the session directly by the RulesExchangeHandler, and thus evaluated (rather than being accessed as a global). See Mapping Facts below.
Mapping Facts
The default Object which is inserted into the rules engine as a fact is the SwitchYard Message's content. However, this can be overridden by specifying your own fact mappings.
IMPORTANT:
If you specify your own fact mappings, the SwitchYard Message content will
not
be inserted as a fact. You will have to add a fact mapping with an expression of "message.content" to have it still included.
Mapping facts from your SwitchYard Exchange, Context or Message for insertion into Drools can be done just like Globals. This can be done via annotions:
@Execute(... inputs={
@Mapping(expression="context['myPropertyFact']", expressionType=ExpressionType.MVEL, contextScope=Scope.IN),
@Mapping(expression="message.content.myNestedFact")
})
public void process(MyData data);
or via XML:
<implementation.rules ...>
<actions>
<action operation="process" type="EXECUTE">
<inputs>
<mapping expression="context['myPropertyFact']" expressionType="MVEL" contextScope="IN"/>
<mapping expression="message.content.myNestedFact"/>
</inputs>
</action>
</actions>
...
</implementation.rules>
Note
-
There is no point in specifying the "variable" attribute of the mappings (as done for global mappings), as the result of each expression is inserted as a nameless fact. For stateless execution, the full list of facts is passed to the StatelessKnowledgeSessions' execute(Iterable) method. For stateful execution, each fact is individually inserted into the StatefulKnowledgeSession. And for Complex Event Processing, each fact is individually inserted into the WorkingMemoryEntryPoint.
-
If the result of the mapping expression implements Iterable (for example, a Collection), then the result is iterated over, and each iteration is inserted as a separate fact, rather than the parent Iterable itself being inserted. This is not recursive behavior (i.e: it is only done once).
Complex Event Processing
Complex Event Processing, or "CEP", is an advanced topic, and can best be explained in the Drools Fusion documentation. What will be shown here in the SwitchYard documentation is how it can be configured via annotation:
...
import org.switchyard.component.rules.annotation.FireUntilHalt;
import org.switchyard.component.rules.annotation.Rules;
@Rules(...
properties={
@Property(name="drools.clockType", value="realtime"),
@Property(name="drools.eventProcessingMode", value="stream",
@Property(name="drools.maxThreads", value="1"),
@Property(name="drools.multithreadEvaluation", value="false")
}
)
public interface MyServiceRules extends MyService {
@FireUntilHalt(id="FooStream")
public void processFooMessage(FooMessage foo);
@FireUntilHalt(id="BarStream")
public void processBarMessage(BarMessage bar);
}
or via XML:
<implementation.rules ...>
<actions>
<action id="FooStream" operation="processFooMessage" type="FIRE_UNTIL_HALT"/>
<action id="BarStream" operation="processBarMessage" type="FIRE_UNTIL_HALT"/>
</actions>
...
<properties>
<property name="drools.clockType" value="realtime"/>
<property name="drools.eventProcessingMode" value="stream"/>
<property name="drools.maxThreads" value="1"/>
<property name="drools.multithreadEvaluation" value="false"/>
</properties>
</implementation.rules>
Auditing a Service
Please see the Listeners and Loggers sections found in the Knowledge Services documentation.
Consuming a Service
Please see the Channels section found in the Knowledge Services documentation.
BPEL Services
The BPEL Component is a pluggable container in SwitchYard which allows a WS-BPEL business process to be exposed as a service through an interface defined using WSDL.
Providing a Service
To provide a service with the BPEL component, you will have to:
-
Define your process using WS-BPEL (e.g. using the Eclipse BPEL editor bundled with JBossTools).
-
Define a WSDL interface for the BPEL service.
-
Define a Deployment Descriptor (e.g. using the ODE Deployment Descriptor editor bundled with JBossTools).
-
Add the component containing the implementation and service interface to the SwitchYard configuration.
Here is an example of the component section of the SwitchYard configuration:
<sca:component name="SayHelloService">
<bpel:implementation.bpel process="sh:SayHello" />
<sca:service name="SayHelloService">
<sca:interface.wsdl interface="SayHelloArtifacts.wsdl#wsdl.porttype(SayHello)"/>
</sca:service>
</sca:component>
The BPEL component contains a single 'implementation.bpel' element that identifies the fully qualified name of the BPEL process.
The component may also contain one or more service elements defining the WSDL port types through which the BPEL process can be accessed.
In the packaged Switchyard application, the BPEL process associated with this fully qualified name, must be present within the root folder of the distribution, along with the deployment descriptor (deploy.xml). An example of the deployment descriptor for the BPEL process referenced above is:
<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03"
xmlns:examples="http://www.jboss.org/bpel/examples" >
<process name="examples:SayHello">
<active>true</active>
<retired>false</retired>
<process-events generate="all"/>
<provide partnerLink="client">
<service name="examples:SayHelloService" port="SayHelloPort"/>
</provide>
</process>
</deploy>
Consuming a Service
This section describes how a BPEL process can invoke other services.
The first step is to define the WSDL interface(s), representing the service(s) to be consumed, using an invoke element within the deployment descriptor, e.g.
<process name="ls:loanApprovalProcess">
<active>true</active>
<process-events generate="all"/>
<provide partnerLink="customer">
<service name="ls:loanService" port="loanService_Port"/>
</provide>
<invoke partnerLink="assessor" usePeer2Peer="false">
<service name="ra:riskAssessor" port="riskAssessor_Port"/>
</invoke>
</process>
The 'usePeer2Peer' property informs the BPEL engine not to use internal communications for sending messages between BPEL processes that may be executing within the same engine, and instead pass messages through the SwitchYard infrastructure.
For each consumed service, we then need to create a reference element within the SwitchYard configuration, to locate the WSDL file and identify the port type associated with the required WSDL service/port.
<sca:component name="loanService">
<bpel:implementation.bpel process="ls:loanApprovalProcess" />
<sca:service name="loanService">
<sca:interface.wsdl interface="loanServicePT.wsdl#wsdl.porttype(loanServicePT)"/>
</sca:service>
<sca:reference name="riskAssessor" >
<sca:interface.wsdl interface="riskAssessmentPT.wsdl#wsdl.porttype(riskAssessmentPT)"/>
</sca:reference>
</sca:component>
Maintain Multiple Versions of a BPEL Process
BPEL processes can be used to implement long lived stateful business processes. However the BPEL process may need to change, over the course of its lifetime, to accomodate new requirements.
This introduces the problem of how to deal with existing active instances of the BPEL process that may not complete for weeks, months or even years. In these situations we need to have a strategy for dealing with multiple version of a BPEL process, to enable new requirements to be introduced, while still preserving the original process definitions associated with existing active process instances.
This is achieved by simply associating a version number with the BPEL process by adding it as a suffix to the BPEL file name. For example, if our BPEL process would normally be located in the file 'HelloWorld.bpel', then we simply add a hyphen followed by the version number, e.g. 'HelloWorld-32.bpel' would indicate that this is the 32nd version of this BPEL process.
When a new version of the BPEL process has been defined, it is packaged in the SwitchYard application, along side the previous versions of the BPEL process. It is important that the older version of the BPEL process remain in the SwitchYard application until there are no longer any active process instances associated with that version.
It is then important that the SwitchYard application is re-deployed, without undeploying the previous version. If the previous version of the SwitchYard application is undeployed, it will cause the BPEL engine to delete all outstanding active instances associated with the process definitions that have bene removed.
Although the BPEL process is versioned, the WSDL interfaces are not. It is important to ensure that any changes made to the WSDL interfaces are backward compatible, so that both the new and older versions of the BPEL (that still have active process instances) are not affected by the changes.
Structure of a SwitchYard BPEL Application
The following image shows the structure of the
say_hello
SwitchYard BPEL quickstart:
The important part is how the artifacts are structured within the src/main/resources folder.
The switchyard.xml configuration file is located in the META-INF folder as usual. However the BPEL deployment descriptor (deploy.xml), and the BPEL process definition are located in the root folder.
The WSDL interface definitions, and any accompanying XSD schemas, can be located in sub-folders. If they are, then the BPEL process and SwitchYard BPEL component configuration must define the correct relative path.